/* 该文件对应的解释：/home/ubuntu/project-practice/Imitate_Muduo_Server/README/TimerWheel.md */

#include <iostream>
#include <memory>
#include <unordered_map>
#include <vector>
#include <functional>
#include <unistd.h>

//这个部分是用来实现时间轮定器的！ -> 先用cpp文件写，并且测试，然后到时候要用的时候改！

using TaskFunc = std::function<void()>;
using ReleaseFunc = std::function<void()>;

//定时器任务类
class TimerTask{
public:
    TimerTask(uint64_t id, int timeout, TaskFunc task_cb)
        :_id(id), _timeout(timeout),_iscanceled(false), _task_cb(task_cb)
    {}

    ~TimerTask(){
        if(!_iscanceled) _task_cb();
        _release();
    }

    void SetCancel(){_iscanceled = true;}

    void SetRelease(const ReleaseFunc& release){_release = release;}

    int GetTimeout(){return _timeout;}
private:

    uint64_t _id;           //每个定时任务的id
    int _timeout;           //设置超时时间，表示定时多久后进行检测
    bool _iscanceled;       //是否取消定时任务 true表示取消， false表示不取消
    TaskFunc _task_cb;      //表示这是当定时器到时后，需要执行的任务
    ReleaseFunc _release;   //表示触发析构的受要做的事情！
};



//时间轮管理超时任务类
class TimerWheel{
private:
    using Task_ShaPtr = std::shared_ptr<TimerTask>;
    using Task_WeaPtr = std::weak_ptr<TimerTask>;
private:
    bool IsTimerExist(uint64_t id){ return _timers.find(id) != _timers.end();}

    void RemoveTimer(uint64_t id){
        //找不到该id对应的任务 无法进行删除
        if(!IsTimerExist(id)) return;
        //反之找得到，直接进行删除
        _timers.erase(id);
    }
public:
    TimerWheel(int capacity = 60)
        :_tick(0), _capacity(capacity), _wheels(_capacity)
    {}

    ~TimerWheel(){}

    //在指定位置添加超时任务
    void TimerAdd(uint64_t id, int timeout, const TaskFunc& cb){
        //如果这个id已经存在了，就没必要再进行插入了
        if(IsTimerExist(id)) return;

        //反之需要：
        //1.先构造一个Task_ShaPtr用于插入_wheels
        Task_ShaPtr sha(new TimerTask(id, timeout, cb));

        //2.为这个新加入的任务设置回调(超时的回调已经设置了（cb）)
        //但是因为超时后，如果真的被析构了，那么_timers内也不能存在对应id的指针！
        sha->SetRelease(std::bind(&TimerWheel::RemoveTimer, this, id));

        //3.搞一个weakptr的，保存在_timers，因为后序刷新的时候用得到！(一定不能用shared版本的！)
        //4.插入weakptr
        _timers[id] = Task_WeaPtr(sha);

        //5.把shared_ptr添加到时间轮
        int pos = (_tick + timeout) & _capacity;
        _wheels[pos].push_back(sha);
    }

    //刷新任务对应的超时时间
    void TimerRefresh(uint64_t id){
        //当前该任务不在，无法刷新
        if(!IsTimerExist(id)) return;
        auto it = _timers.find(id);
        
        //通过lock接口把shared_ptr拿出来()增加引用计数
        Task_ShaPtr pt = it->second.lock();
        //加入时间轮
        int pos = (_tick + pt->GetTimeout()) % _capacity;
        _wheels[pos].push_back(pt);
    }
    

    void TimerRun(){
        _tick = (_tick + 1) % _capacity;
        _wheels[_tick].clear();
    }

    void Cancel(uint64_t id){
        if(!IsTimerExist(id)) return;
        _timers[id].lock()->SetCancel();
    }
private:
    int _tick;                                          //tick指针，指向的是 _wheels数组，指哪执行哪里的任务
    int _capacity;                                      //表示的是轮定数组最大容量
    std::vector<std::vector<Task_ShaPtr>> _wheels;      //用来管理所有的定时任务的
    std::unordered_map<int, Task_WeaPtr>  _timers;      //用weak_ptr来保存shared_ptr的值，方便刷新的时候用
};


//测试部分

class Test{
public:
    Test(){std::cout << "Test()构造" << std::endl;}
    ~Test(){std::cout << "~Test析构" << std::endl;}
};

void DelTest(Test* te){delete te;}

int main(){
    Test *test = new Test;
    TimerWheel tm;
    std::cout << "添加一个定时任务" << std::endl;
    tm.TimerAdd(888, 5, std::bind(DelTest, test));
    for(int i = 0; i < 5; ++i){
        tm.TimerRefresh(888);
        std::cout << "定时任务延时, 5s后超时" << std::endl;
        tm.TimerRun();
        sleep(1);
    }

    //尝试取消id=888对应的超时任务的执行
    tm.Cancel(888);

    while(1){
        std::cout << "-----------------------" << std::endl;
        tm.TimerRun();
        sleep(1);
    }

    return 0;
}



